package cn.java.threadmodel.producerconsumer.lockcondition;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author 小石潭记
 * @date 2021/12/18 10:11
 * @Description: Lock/Condition实现生产者-消费者模型
 */
public class ProducerConsumerQueue<E> {
    /**
     * 队列最大容量
     */
    private final static int QUEUE_MAX_SIZE = 3;
    /**
     * 存放元素的队列
     */
    private Queue<E> queue;

    private final Lock lock = new ReentrantLock();
    private final Condition producerCondition = lock.newCondition();
    private final Condition consumerCondition = lock.newCondition();

    public ProducerConsumerQueue() {
        queue = new LinkedList<>();
    }

    /**
     * 向队列中添加元素
     * @param e
     * @return
     */
    public boolean put(E e) {
        final Lock lock = this.lock;
        lock.lock();
        try {
            while (queue.size() == QUEUE_MAX_SIZE) {
                // 队列已满
                try {
                    producerCondition.await();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
            queue.offer(e);
            System.out.println(Thread.currentThread().getName() + " -> 生产元素，元素个数为：" + queue.size());
            consumerCondition.signal();
        } finally {
            lock.unlock();
        }
        return true;
    }

    /**
     * 从队列中取出元素
     * @return
     */
    public E get() {
        final Lock lock = this.lock;
        lock.lock();
        try {
            while (queue.isEmpty()) {
                // 队列为空
                try {
                    consumerCondition.await();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
            E e = queue.poll();
            System.out.println(Thread.currentThread().getName() + " -> 消费元素，元素个数为：" + queue.size());
            producerCondition.signal();
            return e;
        } finally {
            lock.unlock();
        }
    }
}